package com.pdg.mcplugin.ranger.commandprocessors;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

import com.pdg.mcplugin.common.ArgumentList;
import com.pdg.mcplugin.common.baseclasses.CommandProcessorBase;
import com.pdg.mcplugin.ranger.Ranger;
import com.pdg.mcplugin.ranger.dataproviders.PlaceNameLocation;
import com.pdg.mcplugin.ranger.dataproviders.PublicPlaceNameKey;

public class PublicCommandProcessor extends CommandProcessorBase<Ranger> {
	private static final String MESSAGE_REMEMBER_PLACE_NAME_EXISTS = "That public place name already exists for this world. You can always FORGET it and try again.";
	private static final String MESSAGE_REMEMBER_SUCCESS = "Public place name remembered for this world!";
	private static final String MESSAGE_REMEMBER_FAILURE = "Error! Could not remember public place name for this world!";
	private static final String MESSAGE_REMEMBER_NO_CONSOLE = "Cannot use remember command from console.";
	private static final String MESSAGE_FORGET_PLACE_NAME_DOES_NOT_EXIST = "That public place name does not exist in this world.";
	private static final String MESSAGE_FORGET_SUCCESS = "You have forgotten that public place name.";
	private static final String MESSAGE_FORGET_FAILURE = "Error: public place name not forgotten!";
	private static final String MESSAGE_FORGET_NO_CONSOLE = "Cannot use forget command from the console.";
	private static final String MESSAGE_COMPASS_PLACE_NAME_DOES_NOT_EXIST = "That public place name does not exist in this world.";
	private static final String MESSAGE_COMPASS_SUCCESS = "You have oriented your compass.";
	private static final String MESSAGE_COMPASS_FAILURE = "Error! Could not orient compass towards that public place name for this world!";
	private static final String MESSAGE_COMPASS_NO_CONSOLE = "Cannot orient compass from console.";
	private static final String MESSAGE_FIND_PLACE_NAME_DOES_NOT_EXIST = "That public place name does not exist for this world!";
	private static final String MESSAGE_FIND_FAILURE = "Error: failed to retrieve public place name entry!";
	private static final String MESSAGE_FIND_NO_CONSOLE = "Cannot find public place names from console.";
	private static final String MESSAGE_LIST_NO_CONSOLE = "Cannot list public place names from console.";
	private static final String ERROR_COULD_NOT_LIST_PLACE_NAMES = "Error! Could not list public place names.";
	private static final String MESSAGE_NO_PLACE_NAMES_FOUND = "No public place names found!";
	private static final String MESSAGE_LIST_FILTER = "Showing filtered public list like '%s%%'.";
	private static final String COMMA = ",";
	private static final String ERROR_COULD_NOT_FIND_PLACE_NAMES = "Error! Could not retrieve public place names!";
	private static final String MESSAGE_NEAREST_NO_CONSOLE = "Cannot find nearest public place name from console.";
	private static final String MESSAGE_RENAME_NO_CONSOLE = "Cannot rename places names from console.";
	private static final String MESSAGE_RENAME_NO_SUCH_PLACE = "There is no such place name.";

	public PublicCommandProcessor(Ranger thePlugin) {
		super(thePlugin, new String[]{
				"/ranger public - help for public command (you are here)",
				"/ranger public remember (place name) - remember current location as public (place  name)",
				"/ranger public rename (place name) - starts a renaming conversation for public (place name)",
				"/ranger public find (place name) - give distance and direction to public (place name)",
				"/ranger public forget (place name) - forget the location stored as public (place name)",
				"/ranger public nearest - find the nearest public place name for this world",
				"/ranger public list - list all public place names for this world",
				"/ranger public list (filter) - list public place names for this world that start with (filter)",
				"/ranger public compass (place name) - points your compass to public (place name)"
		});
	}

	@Override
	protected boolean specialProcessCommand(CommandSender theCommandSender,
			String theSubCommand, ArgumentList theArgumentList) {
		if(theSubCommand.equalsIgnoreCase(CommandProcessor.KEY_REMEMBER)){
			return handleRememberSubCommand(theCommandSender,theArgumentList);
		}else if(theSubCommand.equalsIgnoreCase(CommandProcessor.KEY_FORGET)){
			return handleForgetSubCommand(theCommandSender,theArgumentList);
		}else if(theSubCommand.equalsIgnoreCase(CommandProcessor.KEY_RENAME)){
			return handleRenameSubCommand(theCommandSender,theArgumentList);
		}else if(theSubCommand.equalsIgnoreCase(CommandProcessor.KEY_LIST)){
			return handleListSubCommand(theCommandSender,theArgumentList);
		}else if(theSubCommand.equalsIgnoreCase(CommandProcessor.KEY_FIND)){
			return handleFindSubCommand(theCommandSender,theArgumentList);
		}else if(theSubCommand.equalsIgnoreCase(CommandProcessor.KEY_NEAREST)){
			return handleNearestSubCommand(theCommandSender, theArgumentList);
		}else if(theSubCommand.equalsIgnoreCase(CommandProcessor.KEY_COMPASS)){
			return handleCompassSubCommand(theCommandSender, theArgumentList);
		}
		return false;
	}

	private boolean handleCompassSubCommand(CommandSender theCommandSender,
			ArgumentList theArgumentList) {
		if(theCommandSender instanceof Player){
			if(!getPlugin().getPermissionChecker().hasPublicCompassPermission(theCommandSender)) return true;
			if(theArgumentList.size()==0){
				return false;
			}
			Player thePlayer = (Player)theCommandSender;
			
			String thePlaceName = theArgumentList.popAll();
			String theWorldName = thePlayer.getWorld().getName();
			
			PublicPlaceNameKey theKey = new PublicPlaceNameKey(thePlaceName,theWorldName);
			
			if(!getPlugin().getDataProvider().getPublicPlaceNameTableProvider().exists(theKey)){
				getPlugin().sendErrorMessage(theCommandSender, MESSAGE_COMPASS_PLACE_NAME_DOES_NOT_EXIST);
				return true;
			}
			
			PlaceNameLocation thePlaceNameLocation = getPlugin().getDataProvider().getPublicPlaceNameTableProvider().retrieve(theKey);
			
			if(thePlaceNameLocation!=null){
				
				Location theCompassTarget = new Location(thePlayer.getWorld(), thePlaceNameLocation.getX(), thePlaceNameLocation.getY(), thePlaceNameLocation.getZ());
				
				thePlayer.setCompassTarget(theCompassTarget);

				getPlugin().sendSuccessMessage(theCommandSender, MESSAGE_COMPASS_SUCCESS);
			}else{
				getPlugin().sendErrorMessage(theCommandSender, MESSAGE_COMPASS_FAILURE);
			}
			
			return true;
		}else{
			getPlugin().sendErrorMessage(theCommandSender, MESSAGE_COMPASS_NO_CONSOLE);
			return true;
		}
	}

	private boolean handleNearestSubCommand(CommandSender theCommandSender,
			ArgumentList theArgumentList) {
		if(theCommandSender instanceof Player){
			if(!getPlugin().getPermissionChecker().hasPublicNearestPermission(theCommandSender)) return true;
			if(theArgumentList.size()!=0){
				return false;
			}
			Player thePlayer = (Player)theCommandSender;
			Location theLocation = thePlayer.getLocation();
			Map<String,PlaceNameLocation> thePlaceNameLocations = getPlugin().getDataProvider().getPublicPlaceNameTableProvider().getPlaceNames(thePlayer.getWorld().getName());
			if(thePlaceNameLocations==null){
				getPlugin().sendErrorMessage(theCommandSender, ERROR_COULD_NOT_FIND_PLACE_NAMES);
			}else if(thePlaceNameLocations.isEmpty()){
				getPlugin().sendInformationalMessage(theCommandSender, MESSAGE_NO_PLACE_NAMES_FOUND);
			}else{
				String theNearest = null;
				double theDistance = Double.MAX_VALUE;
				Iterator<String> iterator = thePlaceNameLocations.keySet().iterator();
				while(iterator.hasNext()){
					String thePlaceName = iterator.next();
					PlaceNameLocation thePlaceNameLocation = thePlaceNameLocations.get(thePlaceName);
					if(thePlaceNameLocation.getXZDistance(theLocation)<theDistance){
						theNearest=thePlaceName;
						theDistance = thePlaceNameLocation.getXZDistance(theLocation);
					}
				}
				PlaceNameLocation thePlaceNameLocation = thePlaceNameLocations.get(theNearest);
				CommandProcessor.sendDistanceAndDirectionMessage(getPlugin(),theCommandSender,theNearest,thePlaceNameLocation.getRelativeYaw(theLocation),theDistance);
			}
			return true;
		}else{
			getPlugin().sendWarningMessage(theCommandSender, MESSAGE_NEAREST_NO_CONSOLE);
			return true;
		}
	}

	private boolean handleFindSubCommand(CommandSender theCommandSender,
			ArgumentList theArgumentList) {
		if(theCommandSender instanceof Player){
			if(!getPlugin().getPermissionChecker().hasPublicFindPermission(theCommandSender)) return true;
			if(theArgumentList.size()==0){
				return false;
			}
			Player thePlayer = (Player)theCommandSender;
			
			String thePlaceName = theArgumentList.popAll();
			String theWorldName = thePlayer.getWorld().getName();
			
			PublicPlaceNameKey theKey = new PublicPlaceNameKey(thePlaceName,theWorldName);
			
			if(!getPlugin().getDataProvider().getPublicPlaceNameTableProvider().exists(theKey)){
				getPlugin().sendErrorMessage(theCommandSender, MESSAGE_FIND_PLACE_NAME_DOES_NOT_EXIST);
				return true;
			}
			
			PlaceNameLocation thePlaceNameLocation = getPlugin().getDataProvider().getPublicPlaceNameTableProvider().retrieve(theKey);
			
			if(thePlaceNameLocation!=null){
				Location theLocation = thePlayer.getLocation();
				
				double theDistance = thePlaceNameLocation.getXZDistance(theLocation);
				
				double theYaw = thePlaceNameLocation.getRelativeYaw(theLocation);
				
				CommandProcessor.sendDistanceAndDirectionMessage(getPlugin(),theCommandSender,thePlaceName,theYaw,theDistance);
				
			}else{
				getPlugin().sendErrorMessage(theCommandSender, MESSAGE_FIND_FAILURE);
			}
			
			return true;
		}else{
			getPlugin().sendErrorMessage(theCommandSender, MESSAGE_FIND_NO_CONSOLE);
			return true;
		}
	}

	private boolean handleListSubCommand(CommandSender theCommandSender,
			ArgumentList theArgumentList) {
		if(theCommandSender instanceof Player){
			if(!getPlugin().getPermissionChecker().hasPublicListPermission(theCommandSender)) return true;
			Player thePlayer = (Player)theCommandSender;
			List<String> thePlaceNames = null;
			if(theArgumentList.isEmpty()){
				thePlaceNames = getPlugin().getDataProvider().getPublicPlaceNameTableProvider().retrievePlaceNames(thePlayer.getWorld().getName());
			}else{
				String theFilter = theArgumentList.popAll();
				getPlugin().sendInformationalMessage(theCommandSender, String.format(MESSAGE_LIST_FILTER, theFilter ));
				thePlaceNames = getPlugin().getDataProvider().getPublicPlaceNameTableProvider().retrieveFilteredPlacesNames(thePlayer.getWorld().getName(),theFilter);
			}
			if(thePlaceNames==null){
				getPlugin().sendErrorMessage(theCommandSender, ERROR_COULD_NOT_LIST_PLACE_NAMES);
			}else if(thePlaceNames.size()==0){
				getPlugin().sendInformationalMessage(theCommandSender, MESSAGE_NO_PLACE_NAMES_FOUND);
			}else{
				String theList = null;
				Iterator<String> iterator = thePlaceNames.iterator();
				while(iterator.hasNext()){
					if(theList == null){
						theList = iterator.next();
					}else{
						theList+= COMMA + iterator.next();
					}
				}
				getPlugin().sendInformationalMessage(theCommandSender,theList);
			}
			return true;
		}else{
			getPlugin().sendWarningMessage(theCommandSender, MESSAGE_LIST_NO_CONSOLE);
			return true;
		}
	}

	private boolean handleRenameSubCommand(CommandSender theCommandSender,
			ArgumentList theArgumentList) {
		if(theCommandSender instanceof Player){
			if(!getPlugin().getPermissionChecker().hasPublicRenamePermission(theCommandSender)) return true;
			if(theArgumentList.size()==0){
				return false;
			}
			Player thePlayer = (Player)theCommandSender;
			
			String thePlaceName = theArgumentList.popAll();
			
			PublicPlaceNameKey theKey = new PublicPlaceNameKey(thePlaceName,thePlayer.getWorld().getName());
			if(getPlugin().getDataProvider().getPublicPlaceNameTableProvider().exists(theKey)){
				getPlugin().getConversationFactory().startPublicRenameConversation(thePlayer, thePlaceName);
			}else{
				getPlugin().sendErrorMessage(theCommandSender, MESSAGE_RENAME_NO_SUCH_PLACE);
			}
			
			
			return true;
		}else{
			getPlugin().sendErrorMessage(theCommandSender, MESSAGE_RENAME_NO_CONSOLE);
			return true;
		}
	}

	private boolean handleForgetSubCommand(CommandSender theCommandSender,
			ArgumentList theArgumentList) {
		if(theCommandSender instanceof Player){
			if(!getPlugin().getPermissionChecker().hasPublicForgetPermission(theCommandSender)) return true;
			if(theArgumentList.size()==0){
				return false;
			}
			Player thePlayer = (Player)theCommandSender;
			
			String thePlaceName = theArgumentList.popAll();
			String theWorldName = thePlayer.getWorld().getName();
			
			PublicPlaceNameKey theKey = new PublicPlaceNameKey(thePlaceName,theWorldName);
			
			if(!getPlugin().getDataProvider().getPublicPlaceNameTableProvider().exists(theKey)){
				getPlugin().sendErrorMessage(theCommandSender, MESSAGE_FORGET_PLACE_NAME_DOES_NOT_EXIST);
				return true;
			}
			
			if(getPlugin().getDataProvider().getPublicPlaceNameTableProvider().delete(theKey)){
				getPlugin().sendSuccessMessage(theCommandSender, MESSAGE_FORGET_SUCCESS);
			}else{
				getPlugin().sendErrorMessage(theCommandSender, MESSAGE_FORGET_FAILURE);
			}
			
			return true;
		}else{
			getPlugin().sendErrorMessage(theCommandSender, MESSAGE_FORGET_NO_CONSOLE);
			return true;
		}
	}

	private boolean handleRememberSubCommand(CommandSender theCommandSender,
			ArgumentList theArgumentList) {
		if(theCommandSender instanceof Player){
			if(!getPlugin().getPermissionChecker().hasPublicRememberPermission(theCommandSender)) return true;
			if(theArgumentList.size()==0){
				return false;
			}
			Player thePlayer = (Player)theCommandSender;
			Location theLocation = thePlayer.getLocation();
			
			String thePlaceName = theArgumentList.popAll();
			String theWorldName = thePlayer.getWorld().getName();
			
			PublicPlaceNameKey theKey = new PublicPlaceNameKey(thePlaceName,theWorldName);
			
			if(getPlugin().getDataProvider().getPublicPlaceNameTableProvider().exists(theKey)){
				getPlugin().sendErrorMessage(theCommandSender, MESSAGE_REMEMBER_PLACE_NAME_EXISTS);
				return true;
			}
			
			double theX = theLocation.getX();
			double theY = theLocation.getY();
			double theZ = theLocation.getZ();
			
			PlaceNameLocation thePlaceNameLocation = new PlaceNameLocation(theX,theY,theZ);
			
			if(getPlugin().getDataProvider().getPublicPlaceNameTableProvider().create(theKey, thePlaceNameLocation)){
				getPlugin().sendSuccessMessage(theCommandSender, MESSAGE_REMEMBER_SUCCESS);
			}else{
				getPlugin().sendErrorMessage(theCommandSender, MESSAGE_REMEMBER_FAILURE);
			}
			
			return true;
		}else{
			getPlugin().sendWarningMessage(theCommandSender, MESSAGE_REMEMBER_NO_CONSOLE);
			return true;
		}
	}


}
